/**
 * 小程序AI语音助手主页面
 * 实现完整的语音交互闭环：录音 → STT语音转文字 → AI对话 → TTS语音合成 → 音频播放
 * 支持智能导航指令识别和多页面跳转功能
 * 
 * 技术架构：
 * - 前端：Taro + React + TypeScript
 * - 后端：Node.js + Express
 * - AI服务：DeepSeek API集成
 * - 音频处理：Taro音频API + WAV/MP3格式支持
 * 
 * 核心功能模块：
 * 1. 语音录制与权限管理
 * 2. STT语音转文字处理
 * 3. AI对话处理（DeepSeek API）
 * 4. TTS语音合成与播放
 * 5. 智能导航指令解析
 * 6. 数字人状态管理与UI同步
 * 7. 多级错误处理体系
 */

import { useState, useEffect, useRef } from 'react'
import { View, Text, Button, Input } from '@tarojs/components'
import Taro from '@tarojs/taro'
import './index.scss'

// 平台检测工具函数
const isH5 = () => Taro.getEnv() === Taro.ENV_TYPE.WEB
const isMiniProgram = () => Taro.getEnv() === Taro.ENV_TYPE.WEAPP

/**
 * 主页面组件 - 数字助手功能入口
 * 实现语音交互、文本对话、智能导航等核心功能
 */
export default function Index() {
  // ===== 状态管理 =====
  
  /** 数字人情绪状态：idle(空闲) | happy(开心) | listening(录音中) | thinking(处理中) */
  const [digitalHumanMood, setDigitalHumanMood] = useState('idle')
  
  /** 文本输入框内容 */
  const [inputText, setInputText] = useState('')
  
  /** 是否正在处理中（防重复点击） */
  const [isProcessing, setIsProcessing] = useState(false)
  
  // ===== 语音功能状态 =====
  
  /** 是否正在录音中 */
  const [isListening, setIsListening] = useState(false)
  
  /** 录音状态锁（防重复点击） */
  const [isRecordingLocked, setIsRecordingLocked] = useState(false)
  
  /** 录音管理器引用 */
  const recorderRef = useRef<any>(null)
  
  /** 录音超时定时器引用（30秒自动停止保护） */
  const recordingTimeoutRef = useRef<any>(null)

  // ===== 基础配置 =====
  
  /** 后端API基础地址 */
  const BASE_URL = 'http://192.168.8.53:3000' // 修改为你的后端地址

  /**
   * 播放音频缓冲区
   * @param buffer - 音频数据缓冲区
   * @param ext - 音频格式：mp3 | wav | ogg
   */
  const playAudioBuffer = (buffer: ArrayBuffer, ext: 'mp3' | 'wav' | 'ogg' = 'mp3') => {
    try {
      // 验证音频数据有效性
      if (!buffer || buffer.byteLength === 0) {
        Taro.showToast({ title: '音频数据无效', icon: 'none' })
        return
      }
      
      // 获取文件系统管理器
      const fs = Taro.getFileSystemManager()
      
      // 生成临时文件路径
      const filePath = `${Taro.env.USER_DATA_PATH}/reply.${ext}`
      
      // 写入音频文件到临时目录
      fs.writeFileSync(filePath, buffer as any, 'binary')
      
      // 创建音频播放上下文
      const audio = Taro.createInnerAudioContext()
      
      // 设置音频错误处理
      audio.onError((err) => {
        Taro.showToast({ 
          title: `音频播放失败: ${err.errMsg || '未知错误'}`, 
          icon: 'none',
          duration: 3000
        })
      })
      
      // 设置音频播放结束回调（清理临时文件）
      audio.onEnded(() => {
        try {
          fs.unlinkSync(filePath) // 清理临时音频文件
        } catch (e) {
          // 清理失败时静默处理
        }
      })
      
      // 设置音频源并开始播放
      audio.src = filePath
      audio.play()
      
    } catch (e) {
      // 音频播放异常处理
      Taro.showToast({ 
        title: '音频播放失败', 
        icon: 'none',
        duration: 3000
      })
    }
  }

  /**
   * 初始化录音管理器 - 组件挂载时执行
   * 设置录音开始、结束、错误事件监听
   * 仅在小程序环境下初始化
   */
  useEffect(() => {
    // 只在小程序环境下初始化录音管理器
    if (!isMiniProgram()) {
      console.log('H5环境，跳过录音管理器初始化')
      return
    }
    
    // 获取录音管理器实例
    recorderRef.current = Taro.getRecorderManager()
    
    // 录音开始事件监听
    recorderRef.current.onStart(() => {
      console.log('🎤 录音真正开始')
      Taro.showToast({
        title: '🎤 录音已开始',
        icon: 'none',
        duration: 2000
      })
    })
    
    // 录音结束事件监听
    recorderRef.current.onStop((res) => {
      console.log('🎤 录音结束:', res)
      
      // 清除超时定时器
      clearTimeout(recordingTimeoutRef.current)
      
      // 解锁录音状态
      setIsRecordingLocked(false)
      setIsListening(false)
      setDigitalHumanMood('thinking') // 切换到思考状态
      
      Taro.showToast({
        title: '🎤 录音完成，正在处理...',
        icon: 'success',
        duration: 2000
      })
      
      // 处理录音完成的音频文件
      handleVoiceRecordComplete(res.tempFilePath)
    })
    
    // 录音错误事件监听
    recorderRef.current.onError((err) => {
      console.error('🎤 录音错误:', err)
      
      // 清除超时定时器
      clearTimeout(recordingTimeoutRef.current)
      
      // 解锁状态并重置
      setIsRecordingLocked(false)
      setIsListening(false)
      setDigitalHumanMood('idle')
      
      Taro.showToast({
        title: '录音失败，请重试',
        icon: 'none',
        duration: 2000
      })
    })
  }, [])

  /**
   * 数字人表情动画 - 3秒循环切换状态保持活力
   */
  useEffect(() => {
    // 设置3秒间隔的状态切换定时器
    const interval = setInterval(() => {
      setDigitalHumanMood(prev => {
        if (prev === 'idle') return 'happy'
        if (prev === 'happy') return 'idle'
        return 'idle'
      })
    }, 3000)

    // 组件卸载时清理定时器
    return () => clearInterval(interval)
  }, [])

  /**
   * 获取数字人表情符号
   * @returns 对应的表情符号字符串
   */
  const getDigitalHumanExpression = () => {
    switch (digitalHumanMood) {
      case 'happy':
        return '😊'
      case 'idle':
      default:
        return '😊'
    }
  }

  /**
   * 获取数字人状态文字描述
   * @returns 状态描述字符串
   */
  const getDigitalHumanStatus = () => {
    if (isListening) {
      return '正在录音...'
    }
    
    if (isProcessing) {
      return '正在处理...'
    }
    
    switch (digitalHumanMood) {
      case 'happy':
        return '我在听，请说...'
      case 'idle':
      default:
        return '您好，我是您的数字助手'
    }
  }

  /**
   * 开始录音
   * 包含权限检查、状态管理、超时保护
   */
  const startRecord = async () => {
    // 防抖检查 - 如果正在录音或状态被锁定，直接返回
    if (isRecordingLocked || isListening) {
      console.log('录音正在进行中，忽略重复点击')
      return
    }
    
    // 防误触检查 - 如果数字人正在处理或回复，阻止录音
    if (isProcessing) {
      Taro.showToast({
        title: '请等待回复完成',
        icon: 'none',
        duration: 2000
      })
      return
    }
    
    try {
      // 立即锁定状态，防止重复点击
      setIsRecordingLocked(true)
      setIsListening(true)
      setDigitalHumanMood('listening') // 切换到录音状态
      
      // 设置超时保护（30秒后自动停止）
      recordingTimeoutRef.current = setTimeout(() => {
        if (isListening) {
          console.log('录音超时，自动停止')
          stopRecord()
        }
      }, 30000)
      
      // 检查录音权限
      const authRes = await Taro.getSetting()
      if (!authRes.authSetting['scope.record']) {
        const authResult = await Taro.authorize({
          scope: 'scope.record'
        })
        if (authResult.errMsg !== 'authorize:ok') {
          throw new Error('录音权限被拒绝')
        }
      }
      
      // 开始录音（WAV格式，16kHz采样率，单声道）
      recorderRef.current?.start({
        format: 'wav', // 改为wav格式，Dify支持更好
        sampleRate: 16000,
        numberOfChannels: 1,
        frameSize: 50
      })
      
    } catch (error) {
      console.error('录音权限检查失败:', error)
      
      // 错误时解锁状态
      clearTimeout(recordingTimeoutRef.current)
      setIsRecordingLocked(false)
      setIsListening(false)
      setDigitalHumanMood('idle')
      
      Taro.showToast({
        title: '录音权限检查失败',
        icon: 'none',
        duration: 2000
      })
    }
  }

  /**
   * 停止录音
   * 清理超时定时器并停止录音
   */
  const stopRecord = () => {
    try {
      // 清除超时定时器
      clearTimeout(recordingTimeoutRef.current)
      recorderRef.current?.stop()
    } catch (error) {
      // 停止录音失败时也要解锁状态
      setIsRecordingLocked(false)
      setIsListening(false)
      setDigitalHumanMood('idle')
    }
  }

  /**
   * 处理文本输入（公共逻辑提取）
   * 发送文本到后端AI，处理回复并播放TTS
   * @param text - 要处理的文本内容
   */
  const processTextInput = async (text: string) => {
    try {
      // 将识别结果发送到后端进行对话处理
      const chatRes = await Taro.request({
        url: `${BASE_URL}/zpf/text-chatflow`,
        method: 'POST',
        header: { 'Content-Type': 'application/json' },
        data: {
          text: text,
          user: 'miniapp-user'
        },
        timeout: 180000 // 180秒超时（3分钟）
      })
      
      // 检查HTTP状态码
      if (chatRes.statusCode !== 200) {
        const errorData = chatRes.data || {}
        if (errorData.error) {
          throw new Error(`${errorData.error}: ${errorData.detail || errorData.message || '未知错误'}`)
        } else {
          throw new Error(`HTTP ${chatRes.statusCode}: 聊天请求失败`)
        }
      }
      
      const { replyText } = chatRes.data || {}
      if (!replyText) {
        throw new Error('对话回复为空')
      }
      
      // 检查是否是导航指令
      const isNavigation = parseDifyResponse(replyText)
      
      if (isNavigation) {
        console.log('🎯 检测到导航指令，跳过TTS调用')
        setDigitalHumanMood('happy')
        // 导航提示已经在handleNavigation中显示，这里不需要重复
        return // 导航指令不需要TTS
      }
      
      // 普通回复，继续TTS流程
      console.log('💬 普通回复，继续TTS流程')
      
      // 请求 TTS 获取音频
      try {
        const ttsRes = await Taro.request({
          url: `${BASE_URL}/zpf/tts`,
          method: 'POST',
          responseType: 'arraybuffer',
          header: { 'Content-Type': 'application/json' },
          data: { text: replyText, format: 'mp3', voice: 'nova' },
          timeout: 60000 // 60秒超时
        })
        
        // 检查TTS响应状态
        if (ttsRes.statusCode !== 200) {
          throw new Error(`TTS请求失败: ${ttsRes.statusCode}`)
        }
        
        // 检查音频数据
        if (!ttsRes.data || ttsRes.data.byteLength === 0) {
          throw new Error('TTS返回的音频数据为空')
        }
        
        // 播放音频回复
        playAudioBuffer(ttsRes.data as any, 'mp3')
        setDigitalHumanMood('happy')
        
        // 显示成功提示
        Taro.showToast({
          title: '语音回复已播放',
          icon: 'success',
          duration: 2000
        })
        
      } catch (ttsError) {
        // TTS失败，但文本对话成功，显示文本回复
        
        // TTS失败时，显示文本回复而不是完全失败
        const toastMessage = replyText.substring(0, 100) + (replyText.length > 100 ? '...' : '');
        
        Taro.showToast({
          title: toastMessage,
          icon: 'none',
          duration: 4000
        })
        setDigitalHumanMood('happy')
      }
      
    } catch (e) {
      // 检查是否是超时错误
      if (e.errMsg && e.errMsg.includes('timeout')) {
        throw new Error('请求超时，请检查网络连接或稍后重试');
      }
      
      throw e
    }
  }

  /**
   * 处理录音完成
   * 上传录音文件进行STT语音转文字，然后处理识别结果
   * @param tempFilePath - 录音临时文件路径
   */
  const handleVoiceRecordComplete = async (tempFilePath: string) => {
    try {
      setIsProcessing(true)
      
      console.log('🎤 开始处理录音文件:', tempFilePath)
      
      // 1) 先进行语音转文字 (STT)
      const sttRes = await Taro.uploadFile({
        url: `${BASE_URL}/zpf/stt`,
        filePath: tempFilePath,
        name: 'file',
        formData: {
          language: 'zh',
          user: 'miniapp-user'
        }
      })
      
      console.log('🎤 STT响应:', sttRes.data)
      const sttData = JSON.parse(sttRes.data || '{}')
      console.log('🎤 STT解析结果:', sttData)
      
      // 检查STT是否成功
      if (sttData.error) {
        console.error('🎤 STT失败，错误信息:', sttData)
        throw new Error(`语音转文字失败: ${sttData.error} - ${sttData.detail || '未知错误'}`)
      }
      
      const { transcript, text } = sttData
      const recognizedText = transcript || text
      if (!recognizedText) {
        console.error('🎤 STT失败，响应数据:', sttData)
        throw new Error(`语音转文字失败: 未获取到识别结果`)
      }
      
      console.log('🎯 语音识别结果:', recognizedText)
      
      // 2) 处理识别结果
      await processTextInput(recognizedText)
      
    } catch (e) {
      console.error('语音处理失败:', e)
      
      // 根据错误类型显示不同提示
      let errorMessage = '语音处理失败'
      if (e.message?.includes('unsupported_audio_type')) {
        errorMessage = '音频格式不支持，请重新录音'
      } else if (e.message?.includes('stt_failed')) {
        errorMessage = '语音识别失败，请检查网络'
      } else if (e.message?.includes('语音转文字失败')) {
        errorMessage = '语音转文字失败，请重试'
      }
      
      Taro.showToast({ 
        title: errorMessage, 
        icon: 'none',
        duration: 3000
      })
      setDigitalHumanMood('idle')
    } finally {
      setIsProcessing(false)
    }
  }

  /**
   * 处理文本输入提交
   * 用户手动输入文本后的处理逻辑
   */
  const handleTextSubmit = async () => {
    if (inputText.trim()) {
      setDigitalHumanMood('thinking')
      setIsProcessing(true)
      
      try {
        await processTextInput(inputText.trim())
        
        Taro.showToast({
          title: '文本已发送',
          icon: 'success',
          duration: 2000
        })
      } catch (e) {
        
        // 根据错误类型显示不同的提示
        let errorMessage = '文本处理失败'
        
        // 检查是否是Dify相关错误
        if (e.message?.includes('dify_chat_failed')) {
          errorMessage = 'Dify聊天失败，请检查配置'
        } else if (e.message?.includes('empty_reply')) {
          errorMessage = 'AI回复为空，请重试'
        } else if (e.message?.includes('超时')) {
          errorMessage = '请求超时，请检查网络连接或稍后重试'
        } else if (e.message?.includes('TTS')) {
          errorMessage = '语音合成失败，请检查后端配置'
        } else if (e.message?.includes('500')) {
          errorMessage = '服务器内部错误，请检查后端日志'
        } else if (e.message?.includes('网络')) {
          errorMessage = '网络连接失败，请检查网络'
        } else if (e.message?.includes('对话回复为空')) {
          errorMessage = 'AI回复为空，可能是Dify配置问题'
        }
        
        Taro.showToast({ 
          title: errorMessage, 
          icon: 'none',
          duration: 3000
        })
        setDigitalHumanMood('idle')
      } finally {
        setIsProcessing(false)
        setInputText('')
      }
    }
  }

  /**
   * 解析Dify返回的JSON响应
   * 识别导航指令和普通回复
   * @param response - Dify API返回的响应
   * @returns 是否是导航指令
   */
  const parseDifyResponse = (response: any) => {
    try {
      // 检查是否是JSON格式的导航指令
      if (typeof response === 'string') {
        try {
          // 尝试提取JSON部分（如果响应包含其他文本）
          const jsonMatch = response.match(/\{[\s\S]*\}/);
          const jsonString = jsonMatch ? jsonMatch[0] : response;
          
          const jsonResponse = JSON.parse(jsonString);
          
          if (jsonResponse.type === 'navigate') {
            // 延迟执行导航，确保UI更新完成
            setTimeout(() => {
              handleNavigation(jsonResponse.target);
            }, 100);
            return true; // 表示已处理导航
          } else if (jsonResponse.type === 'none') {
            return false; // 表示普通回复
          }
        } catch (parseError) {
          return false;
        }
      } else if (response && typeof response === 'object') {
        // 直接是对象格式
        if (response.type === 'navigate') {
          // 延迟执行导航，确保UI更新完成
          setTimeout(() => {
            handleNavigation(response.target);
          }, 100);
          return true;
        } else if (response.type === 'none') {
          console.log('ℹ️ 无需导航，普通回复');
          return false;
        }
      }
      
      return false; // 默认按普通回复处理
    } catch (error) {
      console.error('❌ 解析Dify响应失败:', error);
      return false;
    }
  };

  /**
   * 处理页面导航
   * 根据导航指令跳转到对应页面
   * @param target - 目标页面标识
   */
  const handleNavigation = (target: string) => {
    // 显示导航提示
    Taro.showToast({
      title: `正在跳转到: ${target}`,
      icon: 'loading',
      duration: 2000
    });
    
    switch (target) {
      case 'repair_application':  // 维修申请
      case 'repair_booking':      // 维修预约
        Taro.navigateTo({ 
          url: '/pages/index/thre',
          success: () => {
            Taro.showToast({ 
              title: '跳转成功！', 
              icon: 'success',
              duration: 1500
            });
          },
          fail: (err) => {
            Taro.showToast({ 
              title: `跳转失败: ${err.errMsg || '未知错误'}`, 
              icon: 'none',
              duration: 3000
            });
          }
        });
        break;
        
      case 'help_center':
        Taro.showToast({ 
          title: '帮助中心功能开发中...', 
          icon: 'none',
          duration: 2000
        });
        break;
        
      case 'settings':
        Taro.showToast({ 
          title: '设置功能开发中...', 
          icon: 'none',
          duration: 2000
        });
        break;
        
      case 'navigation_map':
        Taro.navigateTo({
          url: '/pages/index/ditu',
          success: () => {
            Taro.showToast({ title: '正在跳转到地图页面...', icon: 'success', duration: 1500 });
          },
          fail: (err) => {
            Taro.showToast({ 
              title: `跳转失败: ${err.errMsg || '页面未找到，请重新编译项目'}`, 
              icon: 'none', 
              duration: 3000 
            });
          }
        });
        break;
        
      case 'orders':
        Taro.navigateTo({
          url: '/pages/index/ding',
          success: () => {
            Taro.showToast({ title: '正在跳转到订单页面...', icon: 'success', duration: 1500 });
          },
          fail: (err) => {
            Taro.showToast({ 
              title: `跳转失败: ${err.errMsg || '页面未找到，请重新编译项目'}`, 
              icon: 'none', 
              duration: 3000 
            });
          }
        });
        break;
      default:
        Taro.showToast({ 
          title: `暂不支持跳转到: ${target}`, 
          icon: 'none',
          duration: 2000
        });
        break;
    }
  };

  // ===== H5端简化首页组件 =====
  const renderH5HomePage = () => {
    return (
      <View className="h5-home-container">
        <View className="h5-header">
          <Text className="h5-title">欢迎使用即时修网页版</Text>
          <Text className="h5-subtitle">请在小程序中体验完整功能</Text>
        </View>
        
        <View className="h5-features">
          <View className="h5-feature-item" onClick={() => {
            Taro.showToast({ title: '请在小程序中使用语音功能', icon: 'none', duration: 2000 })
          }}>
            <Text className="h5-feature-icon">🎤</Text>
            <Text className="h5-feature-text">语音助手</Text>
            <Text className="h5-feature-desc">小程序专享</Text>
          </View>
          
          <View className="h5-feature-item" onClick={() => {
            Taro.navigateTo({ 
              url: '/pages/index/thre',
              fail: () => Taro.showToast({ title: '页面跳转失败', icon: 'none' })
            })
          }}>
            <Text className="h5-feature-icon">🔧</Text>
            <Text className="h5-feature-text">维修申请</Text>
            <Text className="h5-feature-desc">点击进入</Text>
          </View>
          
          <View className="h5-feature-item" onClick={() => {
            Taro.navigateTo({ 
              url: '/pages/index/ditu',
              fail: () => Taro.showToast({ title: '页面跳转失败', icon: 'none' })
            })
          }}>
            <Text className="h5-feature-icon">🗺️</Text>
            <Text className="h5-feature-text">导航地图</Text>
            <Text className="h5-feature-desc">点击进入</Text>
          </View>
          
          <View className="h5-feature-item" onClick={() => {
            Taro.navigateTo({ 
              url: '/pages/index/ding',
              fail: () => Taro.showToast({ title: '页面跳转失败', icon: 'none' })
            })
          }}>
            <Text className="h5-feature-icon">📋</Text>
            <Text className="h5-feature-text">我的订单</Text>
            <Text className="h5-feature-desc">点击进入</Text>
          </View>
          
          <View className="h5-feature-item" onClick={() => {
            Taro.navigateTo({ 
              url: '/pages/index/llz',
              fail: () => Taro.showToast({ title: '页面跳转失败', icon: 'none' })
            })
          }}>
            <Text className="h5-feature-icon">🎮</Text>
            <Text className="h5-feature-text">3D模型展示</Text>
            <Text className="h5-feature-desc">点击进入</Text>
          </View>
        </View>
        
        <View className="h5-footer">
          <Text className="h5-footer-text">获得最佳体验，请使用小程序版本</Text>
        </View>
      </View>
    )
  }
  
  // ===== 小程序端数字人首页组件 =====
   const renderMiniProgramHomePage = () => {
     return (
       <View className="digital-human-container">
         {/* 数字人形象区域 */}
        <View className="digital-human-area">
          <View className="digital-human-avatar">
            <View className={`avatar-circle ${digitalHumanMood}`}>
              <Text className="avatar-expression">{getDigitalHumanExpression()}</Text>
            </View>
            <View className="avatar-glow"></View>
          </View>
          
          <View className="digital-human-info">
            <Text className="digital-human-name">小智</Text>
            <Text className="digital-human-status">{getDigitalHumanStatus()}</Text>
            
            {isProcessing && (
              <Text className="processing-status">🔄 处理中...</Text>
            )}
          </View>
          
          {/* 波形条动画 - 录音时显示 */}
          {isListening && (
            <View className="voice-wave-container">
              <View className="wave-bars">
                <View className="wave-bar bar-1"></View>
                <View className="wave-bar bar-2"></View>
                <View className="wave-bar bar-3"></View>
                <View className="wave-bar bar-4"></View>
                <View className="wave-bar bar-5"></View>
                <View className="wave-bar bar-6"></View>
                <View className="wave-bar bar-7"></View>
              </View>
              <Text className="wave-text">正在录音中...</Text>
            </View>
          )}

        </View>

        {/* 语音按钮 */}
        <View className="voice-button-container">
          <Button 
            className={`voice-button ${isListening ? 'recording' : ''}`}
            onTouchStart={startRecord}
            onTouchEnd={stopRecord}
            disabled={isProcessing}
          >
            <Text className="voice-icon">{isListening ? '🎤' : '🎙️'}</Text>
          </Button>
        </View>

        {/* 文本输入区域 */}
        <View className="text-input-container">
          <Input
            className="text-input"
            placeholder="请输入您要说的话..."
            value={inputText}
            onInput={(e) => setInputText(e.detail.value)}
            onConfirm={handleTextSubmit}
          />
          <Button 
            className="send-button"
            onClick={handleTextSubmit}
            disabled={!inputText.trim() || isProcessing}
          >
            发送
          </Button>
        </View>
      </View>
    )
  }
  
  // ===== 主渲染逻辑 =====
  return (
    <View className="index-container">
      {isH5() ? renderH5HomePage() : renderMiniProgramHomePage()}
    </View>
  )
}

